package mapSystem;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import mapSystem.interaction.*;

import tools.Constants;
import tools.GameElement;
import tools.ImageHandler;

public class MapLoader {
    /*Classe que serve para gerar mapas baseados no sistema de tiles com base em arquivos previamente criados*/

	/* Funcao:
     * gerar mapas, retorna o mapa na forma de Tiles *
     
     /* Funcao:
     * gerar mapas, retorna o mapa na forma de Tiles *
     
     * Parametros:
     * Caminho do arquivo com informacoes do mapa
     
     * Retorno: 
     * Retorna um array tridimensional de Tiles que representam o mapa.
     * Caso ocorra algum erro retorna null. 
      
     * Tags:
     * tileset: tilesFloresta.png --> tag que define grupo de tiles a utilizar, seguida pelo grupo a utilizar
     *
     * portals: --------> tag que define mapas dos portais e suas tags utilizadas no basemap
     * {		--------------------> entre chaves:
     *     ...  --------------------> mapa2:2x2:3x0 2x3:3x1 
     *     								^- nome do mapa seguido por ":"
     *     								   depois do primeiro ":" seguem:
     *     										Coordenada do portal no mapa atual ":" coordenada do mapa target
     *     								-------> tag do portal definida de acordo com a linha + 2 (partindo do 0)
     * }
     *
     * basemap: --------> tag que define o mapa de base, tiles andaveis ou nao e suas imagens
     * {(10x10) --------------------> entre parenteses: tamanho do mapa        
     * 	   ...	--------------------> entre chaves:
     *          --------------------> 0:09 <- define acessibilidade do tile antes de ":" 
     *     									  -> 0  = nao andavel
     *     									  -> 1  = andavel
     *     									  -> 2+ = portal
     * }                                   <- define imagem do tile depois de ":"
     * 									   <- coordenadas do tile sao definidas de acordo com linha e coluna escrita:
     *                                        ----> 0:01 0:01 0:01 0:01 <- y = 0 (exemplo de mapa 4x3)
     *                                              0:01 0:03 0:03 0:01 <- y = 1
     *                                              0:01 0:01 0:01 0:01 <- y = 2
     *                                               :    :    :    :
     *                                              x=0  x=1  x=2  x=3
     * 
     * traps:   --------> tag que define localidade das traps
     * {        --------------------> entre chaves:
     *     ...  --------------------> 03<03:07>1 <- define tipo de trap antes de "<"
     *     									  	 <- define coordenada X depois de "<" e antes de ":"
     *     									  	 <- define imagem do trap depois de ":" e antes de ">"
     *     							          	 <- define direcao da trap depois de ">"
     *    										 	----> direcoes: 1, 2, 3, 4
     *    										 <- coordenada Y definida de acordo com a linha (linhas sem traps ficam vasias)
     * }
     * 
     * trigger: --------> tag que define localidade dos triggers
     * {        --------------------> entre chaves:
     *     ...  --------------------> 05:13:9x9 <- define coordenada X antes do primeiro ":"
     *     										<- define imagem do trigger depois do primeiro ":" e antes do segundo ":"
     *     									 	<- define coordenadas do target depois do segundo ":"
     *     									 	<- coordenada Y definida de acordo com linha (linhas sem triggers ficam vasias)
     * }
     * 
     * actions: --------> tag que define localidade de actions
     * {        --------------------> entre chaves:
     *     ...  --------------------> 03<03:07>0x0>1 <- ver trap (desconsiderar direction)
     *     										   	 <- depois de ">"(1): coordenada do target
     *     											 <- depois de ">"(2): se pode ser ativada pelo player
     * }
     * 
     * overmap: --------> tag que define "mapa" sobre todos os outros elementos
     * {		--------------------> entre chaves:
     *     ...  --------------------> 08:09 <- define coordenada X antes de ":"
     * }									<- define imagem do tile(overmap) depois de ":"
     * 										<- coordenada Y definida de acordo com a linha 
     * 
     * ps: Tileset e sempre o primeiro elemento, seguido de basemap, o restante e independente de ordem
     */
	
	
	/* PS: discutir portais mais tarde */
	private static final int NO_TAG = -1;
	private static final int TILESET_TAG = 0;
	private static final int BASEMAP_TAG = 1;
	private static final int OVERMAP_TAG = 2;
	private static final int TRAP_TAG = 3;
	private static final int TRIGGER_TAG = 4;
	private static final int ACTION_TAG = 5;
	private static final int PORTALS_TAG = 6;
		
	public static void load(String mapName, GameMap map)
	{
		if(!checkMap(mapName)) return;
		
		InputStream loader = MapLoader.class.getResourceAsStream("/mapSystem/maps/"+mapName);
		InputStreamReader streamReader = new InputStreamReader(loader);
		BufferedReader reader = new BufferedReader(streamReader);
		
		try {
			read(reader, map);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try
		{
			reader.close();
			streamReader.close();
			loader.close();
		}catch(IOException ioex)
		{
			ioex.printStackTrace();
		}
	}
	
	private static void read(BufferedReader reader, GameMap map) throws IOException {
				
		String curLine = reader.readLine();
		
		BufferedImage tileSet = null;
		
		Tile baseMap[][] = null;
		BufferedImage overMap[][] = null;
		TileAction actions[][] = null;
		
		map.clearMap();
		
		while(curLine != null)
		{
			switch(checkTag(curLine))
			{
				case TILESET_TAG:
					tileSet = loadTileSet(curLine);
					break;
				case BASEMAP_TAG:
					if(tileSet != null) 
					{
						baseMap = setBaseMap(reader, tileSet);
					}
					break;
				case OVERMAP_TAG:
					if(baseMap != null)
					{
						overMap = setOverMap(reader, tileSet, baseMap.length, baseMap[0].length);
					}
					break;
				case TRAP_TAG:
					if(baseMap != null)
					{
						actions = setTraps(reader, actions, tileSet, baseMap.length, baseMap[0].length);
					}
					break;
				case TRIGGER_TAG:
					if(baseMap != null)
					{
						actions = setTriggers(reader, actions, tileSet, baseMap.length, baseMap[0].length);
					}
					break;
				case ACTION_TAG:
					if(baseMap != null)
					{
						actions = setActions(reader, actions, tileSet, baseMap.length, baseMap[0].length);
					}
					break;
				case PORTALS_TAG:
					if(baseMap != null)
					{
						setPortals(reader, map);
					}
					break;
				default: break;
			}
			curLine = reader.readLine();
		}		
		map.setBaseMap(baseMap);
		map.setOverMap(overMap);
		map.setActions(actions);
	}
	
	private static BufferedImage loadTileSet(String line)
	{
		String tileSetName = line.split(":")[1];
		
		return ImageHandler.loadImage("/img/tiles/"+tileSetName);		
	}
	
	private static Tile[][] setBaseMap(BufferedReader reader, BufferedImage tileSet) throws IOException
	{
		Tile map[][] = null;
		
		String curLine = null;
		String data[] = null;
		int access = -1;
		int imgTag = -1;
		BufferedImage tileImage = null;
		
		curLine = reader.readLine();
		curLine = curLine.replace("{(", "");
		curLine = curLine.replace(")", "");
		
		int w = Integer.parseInt(curLine.split("x")[0]);
		int h = Integer.parseInt(curLine.split("x")[1]);
		int xImg = -1;
		int yImg = -1;
		
		map = new Tile[w][h];
		
		for(int i = 0; i < h; i++)
		{
			curLine = reader.readLine();
			data = curLine.split(" ");
			for(int j = 0; j < w; j++)
			{
				access = Integer.parseInt(data[j].split(":")[0]);
				imgTag = Integer.parseInt(data[j].split(":")[1])-1;
				xImg = (imgTag%(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_WIDTH;
				yImg = (imgTag/(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_HEIGHT;
				
				if(xImg+Constants.TILE_WIDTH > tileSet.getWidth()) imgTag = -1;
				if(yImg+Constants.TILE_HEIGHT > tileSet.getHeight()) imgTag = -1;
				
				if(imgTag < 0) tileImage = new BufferedImage(Constants.TILE_WIDTH, Constants.TILE_HEIGHT, BufferedImage.OPAQUE);
				else tileImage = tileSet.getSubimage(xImg, yImg, Constants.TILE_WIDTH, Constants.TILE_HEIGHT);
				
				map[j][i] = new Tile(access, tileImage, j*Constants.TILE_WIDTH, i*Constants.TILE_HEIGHT);
			}
		}
		
		return map;
	}
	private static BufferedImage[][] setOverMap(BufferedReader reader, BufferedImage tileSet, int w, int h) throws IOException
	{
		BufferedImage overMap[][] = new BufferedImage[w][h];
		
		String curLine = reader.readLine(); // feito para pular a primeira linha ( { )
		String data[] = null;
		int x = -1;
		int imgTag = -1;
		int curRow = 0;
		
		int xImg = -1;
		int yImg = -1;
		
		while(curRow < h && !curLine.equals("}"))
		{
			curLine = reader.readLine();
			data = curLine.split(" ");
			if(!curLine.equals("-") && !curLine.equals("}"))
			{
				for(int i = 0; i < data.length; i++)
				{
					x = Integer.parseInt(data[i].split(":")[0]);
					imgTag = Integer.parseInt(data[i].split(":")[1])-1;
					xImg = (imgTag%(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_WIDTH;
					yImg = (imgTag/(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_HEIGHT;
					
					if(xImg+Constants.TILE_WIDTH > tileSet.getWidth()) imgTag = -1;
					if(yImg+Constants.TILE_HEIGHT > tileSet.getHeight()) imgTag = -1;
					
					if(imgTag < 0) overMap[x][curRow] = new BufferedImage(Constants.TILE_WIDTH, Constants.TILE_HEIGHT, BufferedImage.OPAQUE);
					else overMap[x][curRow] = tileSet.getSubimage(xImg, yImg, Constants.TILE_WIDTH, Constants.TILE_HEIGHT);
					
					overMap[x][curRow] = ImageHandler.addColorKey(overMap[x][curRow]);
				}
			}
			curRow++;
		}
		while(!curLine.equals("}")) curLine = reader.readLine();
		
		return overMap;
	}
	private static TileAction[][] setTraps(BufferedReader reader, TileAction actions[][], BufferedImage tileSet, int w, int h) throws IOException
	{
		TileAction r[][] = null;
		if(actions == null)
		{
			actions = new TileAction[w][h];
		}
		
		String curLine = reader.readLine(); // pular uma linha
		String data[] = null;
		int curRow = 0;
		int x = -1;
		int type = -1;
		int direction = -1;
		int imgTag = -1;
		BufferedImage tileImage = null;
		
		int xImg = -1;
		int yImg = -1;
		
		while(!curLine.equals("}") && curRow < h)
		{
			curLine = reader.readLine();
			data = curLine.split(" ");
			if(!curLine.equals("-") && !curLine.equals("}"))
			{
				for(int i = 0; i < data.length; i++)
				{
					type = Integer.parseInt(data[i].split("<")[0]);
					
					data[i] = data[i].split("<")[1];
					x = Integer.parseInt(data[i].split(":")[0]);
					
					data[i] = data[i].split(":")[1];
					imgTag = Integer.parseInt(data[i].split(">")[0])-1;
					xImg = (imgTag%(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_WIDTH;
					yImg = (imgTag/(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_HEIGHT;
					
					if(xImg+Constants.TILE_WIDTH > tileSet.getWidth()) imgTag = -1;
					if(yImg+Constants.TILE_HEIGHT > tileSet.getHeight()) imgTag = -1;
					
					if(imgTag < 0) tileImage = new BufferedImage(Constants.TILE_WIDTH, Constants.TILE_HEIGHT, BufferedImage.OPAQUE);
					else tileImage = tileSet.getSubimage(xImg, yImg, Constants.TILE_WIDTH, Constants.TILE_HEIGHT);
					
					tileImage = ImageHandler.addColorKey(tileImage);

					direction = Integer.parseInt(data[i].split(">")[1]);
					
					actions[x][curRow] = TileHandler.procTrap(type, direction, tileImage, x, curRow);				
				}
			}
			
			
			curRow++;
		}
		while(!curLine.equals("}")) curLine = reader.readLine();
		
		r = actions;
		
		return r;
	}
	private static TileAction[][] setTriggers(BufferedReader reader, TileAction actions[][], BufferedImage tileSet, int w, int h) throws IOException
	{
		TileAction r[][] = null;
		if(actions == null)
		{
			actions = new TileAction[w][h];
		}
		
		String curLine = reader.readLine(); // pular uma linha
		String data[] = null;
		int curRow = 0;
		int x = -1;
		int xt = -1;
		int yt = -1;
		int imgTag = -1;
		BufferedImage tileImage = null;
		
		int xImg = -1;
		int yImg = -1;
		
		while(!curLine.equals("}") && curRow < h)
		{
			curLine = reader.readLine();
			data = curLine.split(" ");
			if(!curLine.equals("-") && !curLine.equals("}"))
			{
				for(int i = 0; i < data.length; i++)
				{					
					x = Integer.parseInt(data[i].split(":")[0]);
					
					imgTag = Integer.parseInt(data[i].split(":")[1])-1;
					xImg = (imgTag%(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_WIDTH;
					yImg = (imgTag/(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_HEIGHT;
					
					if(xImg+Constants.TILE_WIDTH > tileSet.getWidth()) imgTag = -1;
					if(yImg+Constants.TILE_HEIGHT > tileSet.getHeight()) imgTag = -1;
					
					if(imgTag < 0) tileImage = new BufferedImage(Constants.TILE_WIDTH, Constants.TILE_HEIGHT, BufferedImage.OPAQUE);
					else tileImage = tileSet.getSubimage(xImg, yImg, Constants.TILE_WIDTH, Constants.TILE_HEIGHT);
					
					xt = Integer.parseInt(data[i].split(":")[2].split("x")[0]);
					yt = Integer.parseInt(data[i].split(":")[2].split("x")[1]);
					
					actions[x][curRow] = new Trigger(xt, yt, tileImage, x*Constants.TILE_WIDTH, curRow*Constants.TILE_HEIGHT);		
				}
			}			
			
			curRow++;
		}
		while(!curLine.equals("}")) curLine = reader.readLine();
		
		r = actions;
		
		return r;
	}
	private static TileAction[][] setActions(BufferedReader reader, TileAction actions[][], BufferedImage tileSet, int w, int h) throws IOException
	{
		TileAction r[][] = null;
		if(actions == null)
		{
			actions = new TileAction[w][h];
		}
		
		String curLine = reader.readLine(); // pular uma linha
		String data[] = null;
		int curRow = 0;
		int x = -1;
		int type = -1;
		int imgTag = -1;
		BufferedImage tileImage = null;
		int xt = -1;
		int yt = -1;
		int act = 0;
		
		int xImg = -1;
		int yImg = -1;
		
		while(!curLine.equals("}") && curRow < h)
		{
			curLine = reader.readLine();
			data = curLine.split(" ");
			if(!curLine.equals("-") && !curLine.equals("}"))
			{
				for(int i = 0; i < data.length; i++)
				{
					type = Integer.parseInt(data[i].split("<")[0]);
					
					data[i] = data[i].split("<")[1];
					x = Integer.parseInt(data[i].split(":")[0]);
					
					data[i] = data[i].split(":")[1];
					imgTag = Integer.parseInt(data[i].split(">")[0])-1;
					xImg = (imgTag%(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_WIDTH;
					yImg = (imgTag/(tileSet.getWidth()/Constants.TILE_WIDTH))*Constants.TILE_HEIGHT;
					
					if(xImg+Constants.TILE_WIDTH > tileSet.getWidth()) imgTag = -1;
					if(yImg+Constants.TILE_HEIGHT > tileSet.getHeight()) imgTag = -1;
					
					if(imgTag < 0) tileImage = new BufferedImage(Constants.TILE_WIDTH, Constants.TILE_HEIGHT, BufferedImage.OPAQUE);
					else tileImage = tileSet.getSubimage(xImg, yImg, Constants.TILE_WIDTH, Constants.TILE_HEIGHT);
					
					tileImage = ImageHandler.addColorKey(tileImage);
					
					xt = Integer.parseInt(data[i].split(">")[1].split("x")[0]);
					yt = Integer.parseInt(data[i].split(">")[1].split("x")[1]);
					
					act = Integer.parseInt(data[i].split(">")[2]);
					
					actions[x][curRow] = new TileAction(x*Constants.TILE_WIDTH, curRow*Constants.TILE_HEIGHT, 
							xt, yt, tileImage, new GameElement(null, 0, 0, 0, 0, 0, 0, type), act);
				}
			}
			
			
			curRow++;
		}
		while(!curLine.equals("}")) curLine = reader.readLine();
		
		r = actions;
		
		return r;
	}
	private static void setPortals(BufferedReader reader, GameMap map) throws IOException {
		
		int curPortal = 2;
		String curLine = reader.readLine();
		String aux = null;
		String portals[] = null;
		int xp = -1;
		int yp = -1;
		int xt = -1;
		int yt = -1;
		
		while(!curLine.equals("}"))
		{
			curLine = reader.readLine();
			aux = curLine.split(":")[0];
			
			curLine = curLine.replace(aux+":","");
			portals = curLine.split(" ");
			
			if(!curLine.equals("}") && checkMap(aux)) 
			{
				map.addPortal(curPortal, aux);
				for(int i = 0; i < portals.length; i++)
				{
					aux = portals[i].split(":")[0];
					xp = Integer.parseInt(aux.split("x")[0]);
					yp = Integer.parseInt(aux.split("x")[1]);
					
					aux = portals[i].split(":")[1];
					xt = Integer.parseInt(aux.split("x")[0]);
					yt = Integer.parseInt(aux.split("x")[1]);
					
					map.setPortalCoord(xp, yp, xt, yt);
				}
				
				curPortal++;
			}
		}
	}
	
	private static int checkTag(String curLine)
	{
		if(curLine.contains("tileset")) return TILESET_TAG;
		if(curLine.contains("basemap")) return BASEMAP_TAG;
		if(curLine.contains("overmap")) return OVERMAP_TAG;
		if(curLine.contains("trap")) return TRAP_TAG;
		if(curLine.contains("trigger")) return TRIGGER_TAG;
		if(curLine.contains("actions")) return ACTION_TAG;
		if(curLine.contains("portals")) return PORTALS_TAG;
		return NO_TAG;
	}
	private static boolean checkMap(String mapName)
	{
		InputStream fileLoader = MapLoader.class.getResourceAsStream("/mapSystem/maps/"+mapName);
		
		if(fileLoader == null) 
		{
			//System.out.println("Mapa "+mapName+" nao encontrado");
			return false;
		}
		
		try {
			fileLoader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
		
	}
}